import os, sys
import shutil

from configuration import default_path

class TranscoderException(Exception):
  def __init__(self, msg):
    self.msg = msg
  def __str__(self):
    return repr(self.msg)
    
class NoTranscoderForFormatException(TranscoderException):
  pass
  
class TranscoderFailedException(TranscoderException):
  pass


from damn.util import ParseOptionsMap, AssetPathTemplateFormat, AssetPathAbsolute
class BaseTranscoder(object):
  '''
  Notice the lack of ** on kwargs, this function alters kwargs with ParseOptionsMap!
  '''
  def transcode(self, anAssetReference, dstFormat, kwargs): 
    if not os.path.exists(anAssetReference.file.fileName):
      raise TranscoderFailedException("BaseTranscoder: file %s does not exist!"%(anAssetReference.file.fileName))
      
    filePath = self._get_path(anAssetReference, dstFormat, kwargs)
    
    if not os.path.exists(os.path.dirname(filePath)):
      os.makedirs(os.path.dirname(filePath))
    return filePath
  
  def _get_path(self, anAssetReference, dstFormat, kwargs):
    optionsmap = getattr(self, 'options_map', {})
    ParseOptionsMap(optionsmap, anAssetReference.format, kwargs) 
    return AssetPathAbsolute(optionsmap, anAssetReference.format, anAssetReference, dstFormat, kwargs)
      
  def get_path(self, anAssetReference, dstFormat, **kwargs):  
    return self._get_path(anAssetReference, dstFormat, kwargs)
    
  def get_template(self, anAssetReference, dstFormat):    
    return AssetPathTemplateFormat(getattr(self, 'options_map', {}), anAssetReference.format, dstFormat)
    
  def normalize_size(self, kwargs, im):
    size = [None, None]  
    size[0] = kwargs['sizex'] if kwargs['sizex'] else im.size[0]
    size[1] = kwargs['sizey'] if kwargs['sizey'] else im.size[1]
    return size, size[0] != im.size[0] or size[1] != im.size[1]

from yapsy.PluginManager import PluginManager
class Transcoder(PluginManager):
  """
  Provided an asset, convert it to another format.
  """
  transcoders = {}
  
  def __init__(self):
    Transcoder.transcoders = {}
    PluginManager.__init__(self, categories_filter={ "Transcoder": BaseTranscoder}, plugin_info_ext="transcoder")
    path = os.path.join(os.path.abspath(os.path.dirname(__file__)),"transcoders")
    self.setPluginPlaces([path])
    
    self.locatePlugins()
    self.loadPlugins()
    
    for plugin in self.getAllPlugins():
      for src, dst in plugin.plugin_object.convert_map.items():
        if not src in self.transcoders:
          self.transcoders[src] = []
        self.transcoders[src].append(plugin.plugin_object)


  def formats(self, srcFormat):
    formats = []
    class Stub: pass
    stub = Stub()
    stub.format = srcFormat

    if srcFormat in self.transcoders:
      for transcoder in self.transcoders[srcFormat]:
        dsts = transcoder.convert_map[srcFormat]
        dsts = dsts if type(dsts) == type([]) else [dsts]
        for dst in dsts:
          if hasattr(transcoder, 'options_map') and srcFormat in transcoder.options_map:
            formats.append((dst, transcoder.options_map[srcFormat], transcoder.get_template(stub, dst),))
          else:
            formats.append((dst, [], transcoder.get_template(stub, dst),))
    return formats
  
  
  def formats_clean(self, srcFormat):
    formats = self.formats(srcFormat)
  
    import urllib
    
    data = []
    for f, options, template in formats:
      newoptions = []
      for o in options:
        newoptions.append( (str(o[0]), str(getattr(o[1], '__name__', 'unknown type')), str(o[2]),) )
      data.append({'format': f,
                   'format_esc': urllib.quote(f),
                   'options': newoptions,
                   'template': template})
    
    return data
    
    
  def templates(self):
    formats = {}
    class Stub: pass
    stub = Stub()

    for srcFormat, transcoders in self.transcoders.items():
      stub.format = srcFormat
      formats[srcFormat] = {}
      for transcoder in transcoders:
        dsts = transcoder.convert_map[srcFormat]
        dsts = dsts if type(dsts) == type([]) else [dsts]
        for dst in dsts:
          formats[srcFormat][dst] = {'template': transcoder.get_template(stub, dst), 'options':[]}
          if hasattr(transcoder, 'options_map') and srcFormat in transcoder.options_map:
           formats[srcFormat][dst]['options'] = transcoder.options_map[srcFormat]
      if len(formats[srcFormat]) == 0:
        del formats[srcFormat] 
    return formats
  
  def _do(self, anAssetReference, dstFormat, func):
    if anAssetReference.format in self.transcoders:
      dstFailure = True
      for transcoder in self.transcoders[anAssetReference.format]:
        dsts = transcoder.convert_map[anAssetReference.format]
        dsts = dsts if type(dsts) == type([]) else [dsts]
        if dstFormat in dsts:
          dstFailure = False
          result = func(transcoder, anAssetReference, dstFormat)
          if result:
            return result
          #TODO: If failure look for other transcoders with destination aFormat.
      if dstFailure:
        raise NoTranscoderForFormatException("No transcoder for %s -> %s"%(anAssetReference.format, dstFormat))
    else:
      raise NoTranscoderForFormatException("No transcoder for %s -> %s"%(anAssetReference.format, dstFormat))
       
  def transcode(self, anAssetReference, dstFormat, **kwargs):
    return self._do(anAssetReference, dstFormat, lambda transcoder,a,f: transcoder.transcode(a, f, **kwargs))

  def get_paths(self, anAssetReference, dstFormat, **kwargs):
    return self._do(anAssetReference, dstFormat, lambda transcoder,a,f: transcoder.get_path(a, f, **kwargs))
      
  def get_path(self, anAssetReference, dstFormat, **kwargs):  
    paths = self.get_paths(anAssetReference, dstFormat, **kwargs)
    if paths and type(paths) == type([]):
      return paths[0]
    return paths
    
  def get_or_transcode(self, anAssetReference, dstFormat, **kwargs):
    path = self.get_path(anAssetReference, dstFormat, **kwargs)
    created = False
    if not path:
      return path, created
    if not os.path.exists(path):
      path = self.transcode(anAssetReference, dstFormat, **kwargs)
      created = True
    return path, created 

  def delete_asset_cache(self, anAssetReference):
    path = os.path.join(default_path(), 'assets', str(anAssetReference.id))
    if os.path.exists(path):
      print("  Clearing cache!")
      shutil.rmtree(path, True)    
